#include "threadpool.h"
// #include "threadpool.cpp"
#include <functional>
#include <iostream>
#include <thread>
#include <chrono>

const int TASK_MAX_THRESHHOLD = INT32_MAX;
const int THREAD_MAX_THRESHHOLD = 10;
const int THREAD_MAX_IDLE_TIME = 10; // 多久没有新任务,多余的线程被回收

// ThreadPool实现
// ##########################################################
ThreadPool::ThreadPool()
	: initThreadSize_(4),
	  TaskSize_(0),
	  taskQueMaxThreadHold_(TASK_MAX_THRESHHOLD),
	  poolMode_(PoolMode::MODE_FIXED),
	  isPoolRunning_(false),
	  idleThreadSize_(0),
	  threadSizeThreshHold_(THREAD_MAX_THRESHHOLD),
	  curThreadSize_(0)
{
}

ThreadPool::~ThreadPool()
{
	isPoolRunning_ = false;
	std::unique_lock<std::mutex> lock(taskQueMtx_);
	std::cout << "isRunning: flase" << std::endl;
	notEmpty_.notify_all();
	exitCond_.wait(lock, [&]() -> bool
				   { return threads_.size() == 0; });
}

void ThreadPool::setMode(PoolMode poolmode)
{
	if (checkRunningState())
		return;
	poolMode_ = poolmode;
}

bool ThreadPool::checkRunningState() const
{
	return isPoolRunning_;
}

void ThreadPool::setThreadSizeThreshHold(int threshhold)
{
	if (checkRunningState())
		return;
	if (poolMode_ == PoolMode::MODE_CACHED)
		threadSizeThreshHold_ = threshhold;
}

void ThreadPool::setTaskQueMaxThreshHold(int threshHold)
{
	taskQueMaxThreadHold_ = threshHold;
}

Result ThreadPool::submitTask(std::shared_ptr<Task> sp)
{
	std::cout << threads_.size() << std::endl;
	std::unique_lock<std::mutex> lock(taskQueMtx_);
	if (!notFull_.wait_for(lock, std::chrono::seconds(1), [&]() -> bool
						   { return TaskQue_.size() < taskQueMaxThreadHold_; }))
	{
		std::cerr << "task queue is full, submit task fail." << std::endl;
		return Result(sp, false);
	}
	TaskQue_.emplace(sp);

	TaskSize_++;
	// std::cout<<"........."<<TaskQue_.size()<<std::endl;
	std::cout << "submit success" << std::endl;

	notEmpty_.notify_all();
	if (poolMode_ == PoolMode::MODE_CACHED && TaskQue_.size() > idleThreadSize_ && curThreadSize_ < threadSizeThreshHold_)
	{
		std::cout << ">>>> create new thread..." << std::endl;
		auto ptr = std::make_unique<Thread>(std::bind(&ThreadPool::threadFunc, this, std::placeholders::_1));
		int threadId = ptr->getId();
		threads_.emplace(threadId, std::move(ptr));
		// threads_.emplace_back(std::move(ptr));
		threads_[threadId]->start();
		curThreadSize_++;
		idleThreadSize_++;
	}

	return Result(sp, true);
}

void ThreadPool::start(int initThreadSize)
{
	std::cout << TaskQue_.size() << std::endl;
	// exit(0);
	isPoolRunning_ = true;
	initThreadSize_ = initThreadSize;
	curThreadSize_ = initThreadSize;

	for (int i = 0; i < initThreadSize_; i++)
	{
		auto ptr = std::make_unique<Thread>(std::bind(&ThreadPool::threadFunc, this, std::placeholders::_1));
		// threads_.emplace_back(std::move(ptr));
		int threadId = ptr->getId();
		// std::cout<<"threadId :"<<threadId<<std::endl;
		threads_.emplace(threadId, std::move(ptr));
		// std::cout<<"------emplace"<<std::endl;
	}

	for (int i = 0; i < initThreadSize_; i++)
	{
		threads_[i]->start();
		idleThreadSize_++;
	}
	// std::cout<<"##########"<<idleThreadSize_<<std::endl;
}

void ThreadPool::threadFunc(int threadid)
{
	auto lastTime = std::chrono::high_resolution_clock().now();
	// std::cout<<"-----"<<std::this_thread::get_id()<<std::endl;
	// while (isPoolRunning_)
	for(;;)
	{
		std::shared_ptr<Task> task;
		{
			std::unique_lock<std::mutex> lock(taskQueMtx_);
			// std::cout << std::this_thread::get_id() << "try" << std::endl;
			std::cout << std::this_thread::get_id() << "尝试获取任务" << std::endl;
			// std::cout << "TaskQue_---SIZE: " << TaskQue_.size() << std::endl;
			while (TaskQue_.size() == 0)
			{
				if (!isPoolRunning_)
				{
					threads_.erase(threadid);
					std::cout << "threadid: " << std::this_thread::get_id() << "  exit!" << std::endl;
					exitCond_.notify_all();
					return;
				}
				if (poolMode_ == PoolMode::MODE_CACHED)
				{
					if (std::cv_status::timeout == notEmpty_.wait_for(lock, std::chrono::seconds(1)))
					{
						auto now = std::chrono::high_resolution_clock().now();
						auto dur = std::chrono::duration_cast<std::chrono::seconds>(now - lastTime);
						if (dur.count() >= THREAD_MAX_IDLE_TIME && curThreadSize_ > initThreadSize_)
						{
							threads_.erase(threadid);
							curThreadSize_--;
							idleThreadSize_--;
							std::cout << "threadid: " << std::this_thread::get_id() << "  exit!" << std::endl;
							exitCond_.notify_all();
							return;
						}
					}
				}
				else
				{
					notEmpty_.wait(lock);
				}
			}
			std::cout << std::this_thread::get_id() << "获取任务成功" << std::endl;
			task = TaskQue_.front();
			idleThreadSize_--;
			// std::cout<<"#######"<<TaskQue_.size()<<std::endl;
			// if (task != nullptr)
			// 	std::cout << "##########" << std::endl;
			TaskQue_.pop();
			TaskSize_--;
			std::cout<<"剩余数量为: "<<TaskQue_.size()<<std::endl;
			if (TaskQue_.size() > 0)
				notFull_.notify_all();
		}
		if (task != nullptr)
		{
			// task->run();
			task->exec();
			idleThreadSize_++;
		}
		lastTime = std::chrono::high_resolution_clock().now();
	}
}

// ######################################################

// Thread
Thread::Thread(ThreadFunc func)
	: func_(func),
	  threadId_(generateId_++)
{
}

Thread::~Thread()
{
}
void Thread::start()
{
	std::thread t(func_, threadId_);
	std::cout<<"---------"<<std::this_thread::get_id()<<std::endl;
	// std::cout<<"***"<<threadId_<<" "<<std::this_thread::get_id()<<std::endl;
	t.detach();
}

int Thread::generateId_ = 0;

int Thread::getId() const
{
	return threadId_;
}

// #################################################################

Task::Task()
	: result_(nullptr)
{
}

void Task::exec()
{
	if (result_ != nullptr)
	{
		// std::cerr<<"########"<<std::endl;
		result_->setVal(run());
		std::cout<<"任务执行完毕"<<std::endl;
	}
	// result_.setVal(run());
}

void Task::setResult(Result *res)
{
	result_ = res;
}

// #####################################################################

Result::Result(std::shared_ptr<Task> task, bool isValid)
	: task_(task), isValid_(isValid)
{
	task_->setResult(this);
}

Any Result::get()
{
	if (!isValid_)
	{
		return "";
	}
	sem_.wait();
	return std::move(any_);
}

void Result::setVal(Any any)
{
	this->any_ = std::move(any);
	sem_.post();
}

// ######################################